﻿#include "Socket.h"
#include <iostream>
#include "cocos2d.h"
#include "DataPack.h"
#include "Cmd.h"
#include "CmdHandler.h"

#if CC_PLATFORM_WIN32 == CC_TARGET_PLATFORM 
#include <winsock2.h>
#include "windows.h"
#pragma comment(lib,"ws2_32.lib") 
#else

#endif

using namespace std;
using namespace  cocos2d;

SingletonCPP(Socket)

Socket::Socket()
{
	this->InitSocket();

	std::thread t1(std::bind(&Socket::ThreadFunc_Recv, this));
	t1.detach();

	std::thread t2(std::bind(&Socket::ThreadFunc_Send, this));
	t2.detach();
}

void   Socket::InitSocket()
{
	WSADATA wsaData;
	int _error = WSAStartup(MAKEWORD(2, 2), &wsaData);
	this->socket_client = 0;
	this->Connect("127.0.0.1", 8111);
}

void Socket::Connect(const string & ip, int port)
{
	this->ip = ip;
	this->port = port;
	if (socket_client)
	{
		closesocket(socket_client);
		socket_client = 0;
	}
	socket_client = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	this->isConnected = false;

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
	addrSrv.sin_family = AF_INET;

	addrSrv.sin_port = htons(port);

	int len = sizeof(SOCKADDR);
	if (connect(socket_client, (SOCKADDR*)&addrSrv, len) == 0)
	{
		isConnected = true;
		this->OnConnected();
	}
	else
	{
		isConnected = false;
	}
}

void Socket::OnConnected()
{
	cout << "connected ok" << endl;
	this->isDisconnectedCall = false;
}

void Socket::OnDisconnected()
{
	//make sure this event func will be call once per connected
	if (this->isDisconnectedCall || isConnected == false)return;
	this->isDisconnectedCall = true;
	cout << "dis connected " << endl;
}

void Socket::ReConnected()
{
	if (isConnected)return;
	this->Connect(ip, port);
	cout << "try reconnected" << endl;
}

void Socket::ThreadFunc_Recv()
{
	while (true)
	{
		Sleep(1);
		char buf_len[4];
		int ret = ::recv(socket_client, (char*)buf_len, 4, 0);
		if (ret == SOCKET_ERROR)
		{
			this->isConnected = false;
			int code = WSAGetLastError();
			if (code == WSAEWOULDBLOCK)
			{
				//	handler->OnRecvEmptyMessage(this);
			}
			else if (code == WSAENETDOWN)
			{
				this->OnDisconnected();
			}
			else
			{
				this->OnDisconnected();
			}
			Sleep(1000);
			this->ReConnected();
			continue;;
		}

		DataPack *pack = nullptr;
		//	if (ret == 4)
		{
			//接收到了 消息包长度数据
			int mask = 0;
			memcpy(&mask, buf_len, sizeof(int));

			pack = DataPack::CreateWithMask(mask);
			int len = pack->size;
			//cout << "收到数据大小:" << len << endl;
			pack->buffer = new char[len];
			pack->size = len;
			//开始 接收数据
			int ret = ::recv(socket_client, (char*)pack->buffer, len, 0);
			while (ret != SOCKET_ERROR  && ret<len)
			{//未完成IO，继续读
				ret += ::recv(socket_client, (char*)(pack->buffer + ret), len - ret, 0);
			}
		}
		if (ret > 0)
		{
			this->ProcessDataPack(pack);
		}
	}
}

void Socket::ProcessDataPack(DataPack*pack)
{
	if (!pack)return;
	CmdHandler::GetInstance()->PushDataPack(pack);
}

void   Socket::ThreadFunc_Send()
{
	while (true)
	{
		Sleep(1);
		while (sendQueue.empty() == false)
		{
			DataPack *pack = sendQueue.front();
			int ret = ::send(socket_client, pack->buffer, pack->size, 0);

			if (ret == SOCKET_ERROR)
			{
				continue;;
			}
			sendQueue.pop();
			//	pack->release();
		}
	}
}

void Socket::PushDataPack(DataPack *pack)
{
	this->sendQueue.push(pack);
}

void Socket::PushProtoData(const string&data)
{
	DataPack *pack = DataPack::CreateWithString(data);
	this->sendQueue.push(pack);
}


void Socket::PushRawData(const char *data, int size)
{
	DataPack *pack = DataPack::CreateWithRawData(data, size);
	this->sendQueue.push(pack);
}

Socket::~Socket()
{
	this->DisConnected();
}

void Socket::DisConnected()
{
	if (socket_client != 0)
	{
		closesocket(socket_client);
		socket_client = 0;
		isConnected = false;
	}
}